import singleton from "./singleton";
import cache from "./cache";

const _CODE = {
	1000: "未初始化蓝牙",
	10001: "当前蓝牙不可用",
	10002: "没有找到指定设备",
	10003: "连接失败",
	10004: "没有找到指定服务",
	10005: "没有找到指定特征值",
	10006: "当前连接已断开",
	10007: "当前特征值不支持此操作",
	10008: "其余所有系统上报的异常",
	10009: "Android 系统特有",
	10010: "已连接",
	10011: "配对设备需要配对码",
	10012: "连接超时",
	10013: "连接 deviceId 为空或者是格式不正确",
}

const _COMMAND = {
	10: "蓝牙开锁",
	11: "添加指纹",
	12: "删除单个指纹",
	13: "删除所有指纹",
	14: "获取已添加的指纹数",
	15: "获取开锁记录",
	16: "删除所有开锁记录",
	17: "取消指纹登记操作",
	18: "查询当前剩余电量",
	19: "查询可用指纹KEY",
	20: "设置蓝牙名称",
	21: "设置设备恢复出厂设置",
	22: "设置重启设备",
	40: "更新当前时间戳",
}

const _CHACHE_NAME = 'BluetoothTemp'

function _ab2hex(buffer) {
	let hexArr = Array.prototype.map.call(
		new Uint8Array(buffer),
		function(bit) {
			return ('00' + bit.toString(16)).slice(-2)
		}
	)
	return hexArr.join('')
}

//分割字符2个一组
function _splitInPairs(str) {
	return str.match(/.{1,2}/g);
}

//转换成十六进制
function _decimalToHex(decimal) {
	return decimal.toString(16);
}


function __handleSum(d) {
	if (/[A-Za-z]/.test(d)) {
		return parseInt(d, 16)
	} else {
		return Number(d)
	}
}

//计算长度
function _sum(arr) {
	let result = 0;
	for (let i = 0; i < arr.length; i++) {
		if (arr[i].length > 2) {
			let arr2 = _splitInPairs(arr[i])
			for (let j = 0; j < arr2.length; j++) {
				result += parseInt(arr2[j], 16);
			}
		} else {
			result += parseInt(arr[i], 16);
		}
	}
	return _decimalToHex(result);
}


class Bluetooth {

	constructor() {
		//蓝牙开启状态
		this.isOpenBle = false;

		//主动退出
		this.isEnd = true;

		//提示为使用退出系统时长
		this.toast_timeout = "";

		//监听蓝牙适配器状态变化事件
		this.onBluetooth();

		//监听notify 不能重复监听
		this.#onNotify();
	}

	#onNotify() {
		//监听notify返回数据
		uni.onBLECharacteristicValueChange(function(res) {
			let data = _ab2hex(res.value);
			console.log('-------NOTIFY原始数据-------', data)
			let char1 = 'aa55';
			let char2 = '55aa';
			data = data.replace(new RegExp(char1, 'g'), '');
			data = data.replace(new RegExp(char2, 'g'), '');
			data = _splitInPairs(data);
			// let start = 3;
			// let end = start + parseInt(data[0], 16) - 1;
			// console.log(11111, data)
			let len = data[0]
			let command = data[1]
			// let status = data[2]
			data = data.slice(2)

			//注册监听器
			uni.$emit('onBLENotify', {
				len,
				command,
				// status,
				data,
				proto: data.join('')
			})
		
		})
	}


	/**
	 * 向低功耗蓝牙设备特征值中写入二进制数据
	 * @param {String} value
	 * @param {Function} done
	 * @param {Function} error
	 */
	write(command, data = 0, len = 0) {
		let _this = this;
		let obj = cache.get(_CHACHE_NAME);
		if (!obj.deviceId || !obj.serviceId || !obj.writeId || !obj.notifyId) {
			//写入日志
			_this.log('write', `特征数据不全, 请检查连接是否正常`, 'error')
			_this.message("特征数据不全", "error")
			return false;
		}

		console.log(`-------${_COMMAND[command]}-------`)

		let d = _splitInPairs(String(data));


		console.log("写入数据", d)

		let total = _sum([len, command, data]);
		// console.log(111111,d[0])
		// console.log("写入数据", d)
		// let arr = [...['AA', '55', String(len), String(command), total, '55', 'AA']];
		let arr = [];

		if (len == 0) {
			console.log("写入数据", d)
			arr = [...['AA', '55', String(len), String(command), total, '55', 'AA']];
		} else {
			d = d.reverse()
			console.log("带数据写入", d)
			arr = [...['AA', '55', String(len), String(command), ...d, total, '55', 'AA']];
		}
		// let arr = [...['AA', '55', String(len), String(command), ...d, total, '55', 'AA']];
		console.log("完整写入数据", arr)
		let length = arr.length
		//创建buffer
		const buffer = new ArrayBuffer(length)
		const dataView = new DataView(buffer)

		let j = 0;
		for (let i = 0; i < length; i++) {
			let d = parseInt(arr[i], 16);
			if (d < 10) {
				dataView.setUint8(j, 0)
				dataView.setUint8(j++, d)
			} else {
				dataView.setUint8(j++, d)
			}
		}

		return new Promise((resolve, reject) => {
			uni.writeBLECharacteristicValue({
				deviceId: obj.deviceId,
				serviceId: obj.serviceId,
				characteristicId: obj.writeId,
				value: buffer,
				success(r) {
					//写入日志
					_this.log('write', `写入成功：${String(arr)}`, 'success')
					setTimeout(() => {
						resolve(_this.success(`执行成功，写入数据：${_ab2hex(buffer)}`,
							r))
					}, 600)
				},
				fail(e) {

					//写入日志
					_this.log('write', _CODE[e.errCode || e.code] + '。写入命令：' + String(arr),
						'error', e.errCode || e.code)

					console.log('执行命令失败', `写入数据:${_ab2hex(buffer)}`, e)
					_this.err(e, 'error')
					reject(_this.err({
						code: e.code,
						msg: _CODE[e.errCode || e.code]
					}))
				}
			})
		})

	}


	/**
	 *  连接低功耗蓝牙设备 并
	 * 获取蓝牙设备所有服务
	 * 获取蓝牙设备某个服务中所有特征值
	 * @param {Object} deviceId
	 * @param {Object} characteristic
	 */

	connect(deviceId, serviceKey = 0) {
		let _this = this;
		let obj = {}
		console.log('蓝牙连接中', deviceId, serviceKey)

		obj.deviceId = deviceId;
		return new Promise((resolve, reject) => {
			uni.createBLEConnection({
				deviceId,
				success() {
					//写入日志
					_this.log('connect', `SETP1：蓝牙连成功`, 'success')

					function _getServiceId() {
						uni.getBLEDeviceServices({
							deviceId: obj.deviceId,
							success(res) {

								//直接传输无需直接获取
								if (typeof serviceKey == "object") {
									obj.serviceId = serviceKey.serviceId;
									obj.writeId = serviceKey.writeId;
									obj.notifyId = serviceKey.notifyId;
									cache.put(_CHACHE_NAME, obj)
									setTimeout(_openNotify, 200)
									return false;
								}

								let serviceId = res.services[(serviceKey || 0)];
								serviceKey++;
								console.log('走到这步了222', obj, serviceId, res)


								if (!serviceId) {
									// console.log('=======获取serviceId失败=======')
									//写入日志
									_this.log('connect',
										`SETP2：获取serviceId失败, serviceId数组为空`,
										'error')
									reject({
										setp: 2,
										msg: '获取serviceId失败',
										data: res.services
									})
									//结束蓝牙
									_this.end()
									return false
								}
								obj.serviceId = serviceId.uuid;

								//写入日志
								_this.log('connect', `SETP2：获取serviceId成功`,
									'success')


								if (serviceKey > 3) {
									_this.message('获取特征错误', "error")
									//结束蓝牙
									_this.end()
									return false;
								}
								_getCharacteristics()
							},
							fail(e) {
								_this.message(_CODE[e.errCode], "error")

								//写入日志
								_this.log('connect',
									`SETP2：获取serviceId失败, ${_CODE[e.errCode]}`,
									'error')

								reject({
									setp: 2,
									msg: _CODE[e.errCode || e.code],
									data: e
								})
							}
						})
					}

					function _getCharacteristics() {
						uni.getBLEDeviceCharacteristics({
							deviceId: obj.deviceId,
							serviceId: obj.serviceId,
							success(res) {

								console.log("走到这步了3333", res)

								for (let _obj of res.characteristics) {
									//获取notify
									if (_obj.properties.notify && !_obj.uuid.includes(
											'FF02')) {
										obj.notifyId = _obj.uuid;
									}
									//获取writeId
									if (_obj.properties.write && !_obj.uuid.includes(
											'FF01')) {
										obj.writeId = _obj.uuid;
									}
								}

								if (!obj.writeId || !obj.notifyId) {
									serviceKey++
									setTimeout(_ => {
										_getServiceId()
									}, 800)
									return false;
								}

								cache.put(_CHACHE_NAME, obj)

								//写入日志
								_this.log('connect',
									`SETP3：获取characteristics成功`,
									'success')
								_openNotify()
							},
							fail(e) {
								_this.message(_CODE[e.errCode], "error")

								//写入日志
								_this.log('connect',
									`SETP3：获取characteristics失败, ${_CODE[e.errCode]}`,
									'error', e.errCode)

								reject({
									setp: 3,
									msg: _CODE[e.errCode],
									data: e
								})
							}
						})
					}

					//启用低功耗蓝牙notify
					function _openNotify() {
						uni.notifyBLECharacteristicValueChange({
							state: true,
							deviceId: obj.deviceId,
							serviceId: obj.serviceId,
							characteristicId: obj.notifyId,
							success(res) {
								console.log('蓝牙开启成功 已开启notify', obj)
								resolve({
									code: 0,
									msg: 'ok',
									data: obj
								})
							},
							fail(e) {
								console.log('开启notify失败', obj)
								//写入日志
								_this.log('connect',
									`SETP4：开启notify失败, ${_CODE[e.errCode || e.code]}`,
									'error', e.errCode || e.code)
								_this.message(_CODE[e.errCode || e.code],
									"error")
								reject({
									setp: 4,
									msg: _CODE[e.errCode || e.code],
									data: e
								})
							}
						})
					}

					setTimeout(_ => {
						_getServiceId(serviceKey, resolve, reject)
					}, 800)

				},
				fail(e) {
					_this.message(_CODE[e.errCode || e.code], "error")
					//写入日志
					_this.log('connect',
						`SETP1：连接蓝牙失败, ${_CODE[e.errCode || e.code]}`,
						'error', e.errCode || e.code)
					reject({
						setp: 1,
						msg: _CODE[e.errCode || e.code],
						data: e
					})
				}
			})
		})
	}


	/**
	 * 断开与低功耗蓝牙设备的连接。
	 * @param {Function} callback 
	 * @return {void|boolean}
	 */
	end(deviceId, callback) {
		let _this = this
		//更改状态
		_this.isOpenBle = false;
		_this.isEnd = false;

		let data = cache.get(_CHACHE_NAME);
		if (!data) return false;
		deviceId = deviceId || data.deviceId;
		uni.closeBLEConnection({
			deviceId,
			success(r) {
				//写入日志
				_this.log('end', `关闭蓝牙成功`, 'success')
			},
			fail(e) {
				//写入日志
				_this.log('end', `关闭蓝牙失败`, 'error', e.errCode)
			},
			complete() {
				if (typeof callback === 'function') callback()
			}
		})
	}

	/**
	 * 获取已连接蓝牙设备
	 * @param {Function} callback
	 */
	endAll(callback) {
		uni.getConnectedBluetoothDevices({
			success(res) {
				console.log('结束所有连接', res)
				for (let index in res.devices) {
					uni.closeBLEConnection({
						deviceId: res.devices[index].deviceId
					})
				}
				cache.remove(_CHACHE_NAME)
				if (typeof callback === 'function') callback()
			}
		})
	}

	/**
	 * 清理缓存
	 * 
	 * @return {void}
	 */
	clear() {
		cache.remove(_CHACHE_NAME)
	}

	/**
	 * 开始搜寻附近的蓝牙外围设备
	 * @param {Function} done 
	 * @param {Function} error 
	 * @return {void|boolean}
	 */
	start(open, callback, error) {
		let _this = this
		let _interval = "";
		if (open) {
			_this.isOpenBle = true;
		}
		_interval = setInterval(_ => {
			if (_this.isOpenBle) {
				clearInterval(_interval)
				uni.startBluetoothDevicesDiscovery({
					//重复上报
					// allowDuplicatesKey: true,
					success(r) {
						//写入日志
						_this.log('start', `开始搜索蓝牙`, 'success')
						if (typeof callback === 'function') {
							uni.onBluetoothDeviceFound(function(devices) {
								if (typeof callback === 'function') {
									callback(devices)
								}
							})
						}
					},
					fail(e) {
						_this.message("搜索蓝牙失败", 'error')
						//写入日志
						_this.log('end', `搜索蓝牙失败` + _CODE[e.errCode], 'error', e.errCode)
						if (typeof error === 'function') error(e)
					}
				})
			}
		}, 500)
	}

	getBle(callback) {
		let _this = this;
		// let devices = [];
		uni.getBluetoothDevices({
			success(res) {
				if (typeof callback == 'function') {
					callback(res.devices)
				}
			},
			fail(e) {
				_this.message(_CODE[e.errCode], 'error')

				//写入日志
				_this.log('get', `获取蓝牙已发现设备失败` + _CODE[e.errCode], 'error', e.errCode)
			}
		})

	}


	/**
	 * 停止搜寻附近的蓝牙外围设备
	 * @return {void}
	 */
	stop(callback) {
		uni.stopBluetoothDevicesDiscovery({
			success() {
				console.log("停止搜索蓝牙设备")
			},
			complete() {
				if (typeof callback === 'function') {
					callback()
				}
			}
		})
	}

	/**
	 * 监听寻找到新设备的事件
	 * @param {Function} callback
	 */
	watch(callback) {
		console.log('蓝牙监听中...')
		uni.onBluetoothDeviceFound(function(devices) {
			if (typeof callback === 'function') {
				callback(devices)
			}
		})
	}

	/**
	 * 初始化蓝牙模块
	 * @return {void}
	 */
	initialization(callback) {
		let _this = this;
		_this.isOpenBle = false;
		uni.openBluetoothAdapter({
			success(res) {
				//写入日志
				_this.log('init', `蓝牙初始化完成`, 'success')

				_this.isOpenBle = true;
				if (typeof callback == 'function') {
					callback('success')
				}
			},
			fail(e) {
				e.code = e.code || '1000'
				//写入日志
				_this.log('init', `蓝牙初始化失败, ` + _CODE[e.errCode || e.code], 'error', e.errCode)
				_this.isOpenBle = false;
				_this.stop()
				_this.close()

				if (e.code == '1000') {
					setTimeout(() => {
						_this.initialization()
					}, 1000)
					return false;
				}
				if (typeof callback == 'function') {
					callback('error')
				}
			}
		})
	}



	/**
	 * 监听蓝牙适配器状态变化事件
	 * @return {void}
	 */
	onBluetooth() {
		let _this = this;
		uni.onBluetoothAdapterStateChange(function(res) {
			console.log("监听蓝牙状态变化", res)
			if (!res.available) {
				_this.isOpenBle = false;
				_this.stop();
				_this.close();
				_this.message("蓝牙已断开", "error");

			}
		})

		uni.onBLEConnectionStateChange(function(res) {
			// 该方法回调中可以用于处理连接意外断开等异常情况
			console.log(`!!!!设备连接状态: ${res.connected}`, res)
			uni.$emit('onBLEConnectionState', res)
			// if (!res.connected) {
			// 	_this.toast_timeout = setTimeout(() => {
			// 		uni.showModal({
			// 			title: '系统提示',
			// 			content: '您长时间未操作连接已断开。',
			// 			confirmText: "返回首页",
			// 			success: function(res) {
			// 				if (res.confirm) {
			// 					uni.switchTab({
			// 						url: '/pages/tabbar/index/index'
			// 					});
			// 				}
			// 			}
			// 		});
			// 	}, 1000)
			// }

		})
	}
	/**
	 * 清除退出提示
	 */
	clearToast() {
		setTimeout(() => {
			clearTimeout(this.toast_timeout)
		}, 100)

	}

	/**
	 * 关闭蓝牙模块。
	 */
	close() {
		// console.log('关闭蓝牙')
		this.isOpenBle = false;
		uni.closeBluetoothAdapter()
	}

	/**
	 * 消息提示
	 * @param {String} title 
	 * @param {String} icon 
	 * @return {void}
	 */
	message(title, icon = 'none') {
		if (!title) return;
		uni.showToast({
			title,
			icon
		})
	}



	/**
	 * 生成日志
	 * @param {string} category
	 * @param {string} content  
	 * @param {string} status
	 * @param {number} [code=0] 
	 * @return {void} 
	 */
	log(category, content, status, code = 0) {
		let data = {
			category,
			content,
			status,
			code
		}
		if (status == 'error' || status == 'connect') {
			console.log(`=======LOG--${status.toUpperCase()}=======`, data)
		}
		// console.log(data)
	}


	success(msg, data = [], code = 0) {
		return {
			code,
			msg,
			data
		}
	}

	err(e) {
		if (e.code == 10000) return false;
		this.message(_CODE[e.errCode || e.code], 'error')
		return {
			code: e.errCode || e.code,
			msg: _CODE[e.errCode || e.code],
			data: e
		}
	}
}

export default singleton(Bluetooth)